﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace FourPartHarmony3 {
    class ChordRenderer {
        HorizontalVirtualCanvas mScoreCanvas;

        public void Initialize(HorizontalVirtualCanvas canvas) {
            mScoreCanvas = canvas;
        }

        private static ScoreElementType MeterNumberToScoreElement(int number) {
            switch (number) {
            case 2: return ScoreElementType.METER_2;
            case 3: return ScoreElementType.METER_3;
            case 4: return ScoreElementType.METER_4;
            case 5: return ScoreElementType.METER_5;
            case 6: return ScoreElementType.METER_6;
            case 7: return ScoreElementType.METER_7;
            case 8: return ScoreElementType.METER_8;
            default:
                System.Diagnostics.Debug.Assert(false);
                return ScoreElementType.CENTER_DOT;
            }
        }

        static int [] keyGFlat7  = new int [] { 4,1,5,2,6,3,7 };
        static int [] keyGSharp7 = new int [] { 0,3,-1,2,5,1,4 };

        private void DrawKeySharp(Vector xy, int num) {
            for (int i = 0; i < num; ++i) {
                AddPathToScore(ScoreElementType.SHARP, new Vector(xy.X + i * Constants.KEY_SYMBOL_WIDTH,
                    xy.Y + Constants.LINE_G_TOP + Constants.LINE_TO_LINE_PIXEL / 2 * keyGSharp7[i]));
            }
            for (int i = 0; i < num; ++i) {
                AddPathToScore(ScoreElementType.SHARP, new Vector(xy.X + i * Constants.KEY_SYMBOL_WIDTH,
                    xy.Y + Constants.LINE_F_TOP + Constants.LINE_TO_LINE_PIXEL / 2 * (keyGSharp7[i] + 2)));
            }
        }

        private void DrawKeyFlat(Vector xy, int num) {
            for (int i = 0; i < num; ++i) {
                AddPathToScore(ScoreElementType.FLAT, new Vector(xy.X + i * Constants.KEY_SYMBOL_WIDTH,
                    xy.Y + Constants.LINE_G_TOP + Constants.LINE_TO_LINE_PIXEL / 2 * keyGFlat7[i]));
            }
            for (int i = 0; i < num; ++i) {
                AddPathToScore(ScoreElementType.FLAT, new Vector(xy.X + i * Constants.KEY_SYMBOL_WIDTH,
                    xy.Y + Constants.LINE_F_TOP + Constants.LINE_TO_LINE_PIXEL / 2 * (keyGFlat7[i] + 2)));
            }
        }

        private void AddPathToScore(ScoreElementType se, Vector pos) {
            mScoreCanvas.AddVirtualChild(new VirtualScoreElement(ScoreElementPathes.PathItem(se), pos));
        }
        
        private void AddTextToScore(string text, double fontSize, Vector pos) {
            var vs = new VirtualShape(VirtualShape.Type.TextBlock, pos);
            vs.SetText(text, fontSize);
            mScoreCanvas.AddVirtualChild(vs);
        }

        private void AddTextToScoreCenter(string text, double fontSize, Vector pos, double strokeThickness) {
            var vs = new VirtualShape(VirtualShape.Type.TextBlock, pos);
            vs.SetText(text, fontSize);
            vs.Bounds = new Rect(vs.Bounds.X - vs.Bounds.Width / 2, vs.Bounds.Y - vs.Bounds.Height / 2,
                    vs.Bounds.Width, vs.Bounds.Height);
            if (strokeThickness != 0.0) {
                vs.StrokeThickness = strokeThickness;
                vs.Stroke = Brushes.Black;
            }
            mScoreCanvas.AddVirtualChild(vs);
        }
        
        private void DrawClef(Chord c, StaffStatus ss) {
            MusicKeyInfo mki = new MusicKeyInfo(c.MusicKey, KeyRelation.I調);

            // ト音記号
            AddPathToScore(ScoreElementType.GCLEF, new Vector(
                    ss.Pos.X + Constants.LINE_LEFT + Constants.GCLEF_OFFSET_X,
                    ss.Pos.Y + Constants.LINE_G_TOP + 3 * Constants.LINE_TO_LINE_PIXEL));
            // ヘ音記号
            AddPathToScore(ScoreElementType.FCLEF, new Vector(
                    ss.Pos.X + Constants.LINE_LEFT + Constants.FCLEF_OFFSET_X,
                    ss.Pos.Y + Constants.LINE_F_TOP + 1 * Constants.LINE_TO_LINE_PIXEL));

            // 調名
            AddTextToScore(string.Format("{0}:", mki.GetString()), Constants.KEY_FONT_SIZE,
                    new Vector(ss.Pos.X + Constants.KEY_FONT_OFFSET_X, ss.Pos.Y+Constants.KEY_FONT_OFFSET_Y));

            ss.AdvanceX(Constants.STAFF_OFFSET_X);

            // 調のシャープとフラット
            string sKey = mki.GetString() + ":";
            if (0 < mki.FlatNum()) {
                DrawKeyFlat(ss.Pos, mki.FlatNum());
            }
            if (0 < mki.SharpNum()) {
                DrawKeySharp(ss.Pos, mki.SharpNum());
            }

            ss.SetKeySharpFlat(mki.CreateSharpLetterNameList(), mki.CreateFlatLetterNameList());

            ss.AdvanceX((mki.FlatNum()+mki.SharpNum())*Constants.KEY_SYMBOL_WIDTH);
        }

        public void Draw(Chord prevChord, Chord c, StaffStatus ss) {
            if (ss.ShouldDrawClef) {
                DrawClef(c, ss);
                ss.ShouldDrawClef = false;
            }

            if (ss.ShouldDrawMeter) {
                AddPathToScore(MeterNumberToScoreElement(ss.Meter.measure), new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_G_TOP + 1 * Constants.LINE_TO_LINE_PIXEL));
                AddPathToScore(MeterNumberToScoreElement(ss.Meter.length),  new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_G_TOP + 3 * Constants.LINE_TO_LINE_PIXEL));
                AddPathToScore(MeterNumberToScoreElement(ss.Meter.measure), new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_F_TOP + 1 * Constants.LINE_TO_LINE_PIXEL));
                AddPathToScore(MeterNumberToScoreElement(ss.Meter.length),  new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_F_TOP + 3 * Constants.LINE_TO_LINE_PIXEL));
                ss.ShouldDrawMeter = false;
            }

            ss.AdvanceX(Constants.CHORD_WIDTH_HALF_X);

            if (prevChord == null || (prevChord.KeyRelation == KeyRelation.I調 && c.KeyRelation != KeyRelation.I調)) {
                DrawInternalKeyStart(c, ss);
            } else if (prevChord.KeyRelation != c.KeyRelation) {
                DrawInternalKeyChange(c, ss);
            }

            DrawNote(ss, c, Part.Bas);
            DrawNote(ss, c, Part.Ten);
            DrawNote(ss, c, Part.Alt);
            DrawNote(ss, c, Part.Sop);

            DrawChordSymbol(ss, c);
            DrawVerdict(ss, c);

            DrawCadence(ss, c);

            AddChordFocus(ss, c);

            ss.AdvanceX(Constants.CHORD_WIDTH_HALF_X);
        }

        public void DrawEnd(Chord lastChord, StaffStatus ss) {
            if (lastChord == null) {
                return;
            }

            if (lastChord.KeyRelation != KeyRelation.I調) {
                DrawInternalKeyEnd(lastChord, ss);
            }
        }

        // ト音記号で下のドからの距離(度+1) 高い音＝数字が大きい
        private int PitchToGClefY(Pitch p) {
            if (p.Octave < 3) {
                return -1;
            }
            return (p.Octave - 4) * 7 + p.NoteDistanceFromC();
        }

        //　ヘ音記号で真ん中のドからの距離(度+1) 高い音＝数字が大きい
        private int PitchToFClefY(Pitch p) {
            if (p.Octave < 1) {
                return -1;
            }
            return (p.Octave - 3) * 7 + p.NoteDistanceFromC();
        }

        private double PitchAndPartToYPixel(Pitch pitch, Part part) {
            switch (part) {
            case Part.Sop:
            case Part.Alt:
                return Constants.G_O4C_Y_PIXEL - Constants.LINE_TO_LINE_PIXEL / 2 * PitchToGClefY(pitch);
            case Part.Ten:
            case Part.Bas:
                return Constants.F_O3C_Y_PIXEL - Constants.LINE_TO_LINE_PIXEL / 2 * PitchToFClefY(pitch);
            default:
                System.Diagnostics.Debug.Assert(false);
                break;
            }
            return -1;
        }

        static Pitch UPPER_A  = new Pitch(new LnDegInversion(LN.A), 5);
        static Pitch CENTER_C = new Pitch(new LnDegInversion(LN.C), 4);
        static Pitch LOWER_E  = new Pitch(new LnDegInversion(LN.E), 2);

        private void DrawLedgerLines(StaffStatus ss, Pitch pitch, Part part) {
            switch (part) {
            case Part.Sop:
            case Part.Alt: {
                    for (int i = 0; i <= (PitchToGClefY(pitch) - PitchToGClefY(UPPER_A)) / 2; ++i) {
                        AddPathToScore(ScoreElementType.LEDGER_LINE, new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_G_TOP - Constants.LINE_TO_LINE_PIXEL * (i + 1)));
                    }
                    for (int i = 0; i <= (PitchToGClefY(CENTER_C) - PitchToGClefY(pitch)) / 2; ++i) {
                        AddPathToScore(ScoreElementType.LEDGER_LINE, new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_G_BOTTOM + Constants.LINE_TO_LINE_PIXEL * (i + 1)));
                    }
                }
                break;
            case Part.Ten:
            case Part.Bas: {
                    for (int i = 0; i <= (PitchToFClefY(pitch) - PitchToFClefY(CENTER_C)) / 2; ++i) {
                        AddPathToScore(ScoreElementType.LEDGER_LINE, new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_F_TOP - Constants.LINE_TO_LINE_PIXEL * (i + 1)));
                    }
                    for (int i = 0; i <= (PitchToFClefY(LOWER_E) - PitchToFClefY(pitch)) / 2; ++i) {
                        AddPathToScore(ScoreElementType.LEDGER_LINE, new Vector(ss.Pos.X, ss.Pos.Y + Constants.LINE_F_BOTTOM + Constants.LINE_TO_LINE_PIXEL * (i + 1)));
                    }
                }
                break;
            default:
                break;
            }
        }

        private void DrawDegree(Vector notePos, int degree) {
            var se = (ScoreElementType)(ScoreElementType.SMALL_DEGREE_1 + (degree - 1));
            AddPathToScore(se, notePos);
        }

        private ScoreElementType ChortPartToNoteElement(StaffStatus ss, Chord chord, Part part) {
            switch (part) {
            case Part.Sop:
            case Part.Ten:
                switch (chord.Duration) {
                case DurationType.Whole: return ScoreElementType.NOTE_1;
                case DurationType.Half: return ScoreElementType.NOTE_2;
                case DurationType.Quarter: return ScoreElementType.NOTE_4;
                case DurationType.Eighth: return ScoreElementType.NOTE_8;
                default: System.Diagnostics.Debug.Assert(false); return ScoreElementType.NOTE_2;
                }
            case Part.Alt:
            case Part.Bas:
                switch (chord.Duration) {
                case DurationType.Whole: return ScoreElementType.NOTE_1;
                case DurationType.Half: return ScoreElementType.NOTE_2_INV;
                case DurationType.Quarter: return ScoreElementType.NOTE_4_INV;
                case DurationType.Eighth: return ScoreElementType.NOTE_8_INV;
                default: System.Diagnostics.Debug.Assert(false); return ScoreElementType.NOTE_2_INV;
                }
            default:
                System.Diagnostics.Debug.Assert(false); return ScoreElementType.NOTE_2;
            }
        }

        /// <summary>
        /// 2つのピッチのオクターブとLetter Nameが同じときtrue
        /// </summary>
        private static bool IsSameLnOctave(Pitch pitch0, Pitch pitch1) {
            return pitch0.LetterName.LN == pitch1.LetterName.LN && pitch0.Octave == pitch1.Octave;
        }

        private static bool IsNeedToDrawAccidental(Chord chord, Part part) {
            switch (part) {
            case Part.Bas:
            case Part.Alt:
                return true;
            case Part.Ten:
                return !IsSameLnOctave(chord.GetPitch(Part.Bas), chord.GetPitch(Part.Ten));
            case Part.Sop:
                return !IsSameLnOctave(chord.GetPitch(Part.Alt), chord.GetPitch(Part.Sop));
            default:
                System.Diagnostics.Debug.Assert(false);
                return true;
            }
        }

        /// <param name="part">Bas Ten Alt Sopの順で呼び出される。</param>
        private void DrawNote(StaffStatus ss, Chord chord, Part part) {
            var pitch = chord.GetPitch(part);

            // Noteの位置。
            var notePos = new Vector(
                ss.Pos.X,
                ss.Pos.Y + PitchAndPartToYPixel(pitch, part));

            // 臨時記号の位置。
            var accidentalPos = new Vector(notePos.X, notePos.Y);

            // Noteの位置とAccidentalの位置を調節する。
            if (part == Part.Bas || part == Part.Alt) {
                if (chord.GetPitch((Part)part + 1).AbsIntervalNumberWith(pitch) <= 1) {
                    // 玉の位置が1個上の音符と隣接している(2音以内)ので、下の玉を右にずらす。
                    notePos.X += Constants.ACCIDENTAL_OFFSET_X;
                }
                if (chord.GetPitch((Part)part + 1).AbsIntervalNumberWith(pitch) <= 2) {
                    // 玉の位置が1個上の音符と隣接している(3音以内)ので、臨時記号を左にずらす
                    accidentalPos.X -= Constants.ACCIDENTAL_OFFSET_X;
                }
            }

            // Note描画。
            AddPathToScore(ChortPartToNoteElement(ss, chord, part), notePos);

            if (IsNeedToDrawAccidental(chord, part)) {
                if (pitch.IsFlat() && !ss.IsLnFlat(pitch.NaturalLetterName())) {
                    // 臨時フラット
                    AddPathToScore(ScoreElementType.FLAT, accidentalPos);
                    ss.AddAccidentalFlat(pitch.NaturalLetterName());
                }
                if (pitch.IsSharp() && !ss.IsLnSharp(pitch.NaturalLetterName())) {
                    // 臨時シャープ
                    AddPathToScore(ScoreElementType.SHARP, accidentalPos);
                    ss.AddAccidentalSharp(pitch.NaturalLetterName());
                }
                if (pitch.IsNatural()
                        && (ss.IsLnFlat(pitch.NaturalLetterName())
                            || ss.IsLnSharp(pitch.NaturalLetterName()))) {
                    // 臨時ナチュラル
                    AddPathToScore(ScoreElementType.NATURAL, accidentalPos);
                    ss.AddAccidentalNatural(pitch.NaturalLetterName());
                }
                if (pitch.IsDoubleSharp()) {
                    // ダブルシャープ。常に臨時でつける
                    AddPathToScore(ScoreElementType.DOUBLE_SHARP, accidentalPos);
                }
                if (pitch.IsDoubleFlat()) {
                    AddPathToScore(ScoreElementType.DOUBLE_FLAT, accidentalPos);
                }
            }

            // 音度
            if (part == Part.Bas || part == Part.Alt ||
                    chord.GetPitch((Part)part - 1).AbsIntervalNumberWith(pitch) != 0) {
                // TenかSopの場合で、1つ下の音符と同度の場合音度を出さない。
                DrawDegree(notePos, pitch.Degree);
            }

            // 補助線
            DrawLedgerLines(ss, pitch, part);
        }

        private void DrawInternalKeyStart(Chord c, StaffStatus ss) {
            if (c.KeyRelation == KeyRelation.I調) {
                return;
            }

            var pos = new Vector(
                    ss.Pos.X + Constants.INTERNAL_KEY_OFFSET_X,
                    ss.Pos.Y + Constants.CHORD_SYMBOL_OFFSET_Y);
            foreach (var path in mInternalKeyPathArray[(int)c.KeyRelation]) {
                AddPathToScore(path, pos);
            }

            var posParen = new Vector(
                ss.Pos.X + Constants.INTERNAL_KEY_OFFSET_X + Constants.LINE_TO_LINE_PIXEL,
                ss.Pos.Y + Constants.CHORD_SYMBOL_OFFSET_Y);
            AddPathToScore(ScoreElementType.INTERNAL_KEY_START, posParen);
        }

        private void DrawInternalKeyChange(Chord c, StaffStatus ss) {
            var pos = new Vector(
                    ss.Pos.X - Constants.CHORD_WIDTH_HALF_X,
                    ss.Pos.Y + Constants.CHORD_SYMBOL_OFFSET_Y);
            AddPathToScore(ScoreElementType.INTERNAL_KEY_END, pos);

            DrawInternalKeyStart(c, ss);
        }

        private void DrawInternalKeyEnd(Chord c, StaffStatus ss) {
            var pos = new Vector(
                    ss.Pos.X,
                    ss.Pos.Y + Constants.CHORD_SYMBOL_OFFSET_Y);

            AddPathToScore(ScoreElementType.INTERNAL_KEY_END, pos);
        }

        private void DrawChordSymbol(StaffStatus ss, Chord c) {
            var pos = new Vector(
                    ss.Pos.X + Constants.CHORD_SYMBOL_OFFSET_X,
                    ss.Pos.Y + Constants.CHORD_SYMBOL_OFFSET_Y);

            switch (c.ChordDegree) {
            case CD.I:   AddPathToScore(ScoreElementType.CHORD_1, pos); break;
            case CD.II:  AddPathToScore(ScoreElementType.CHORD_2, pos); break;
            case CD.III: AddPathToScore(ScoreElementType.CHORD_3, pos); break;
            case CD.IV:  AddPathToScore(ScoreElementType.CHORD_4, pos); break;
            case CD.V:   AddPathToScore(ScoreElementType.CHORD_5, pos); break;
            case CD.VI:  AddPathToScore(ScoreElementType.CHORD_6, pos); break;
            case CD.VII: AddPathToScore(ScoreElementType.CHORD_7, pos); break;
            case CD.V_V:
                AddPathToScore(ScoreElementType.CHORD_5, pos);
                AddPathToScore(ScoreElementType.UPPER_5, pos);
                break;
            }

            switch (c.NumberOfNotes) {
            case NumberOfNotes.Triad: break;
            case NumberOfNotes.Seventh: AddPathToScore(ScoreElementType.SEVENTH, pos); break;
            case NumberOfNotes.Ninth:   AddPathToScore(ScoreElementType.NINTH, pos); break;
            }

            switch (c.Inversion) {
            case Inversion.根音: break;
            case Inversion.第3音: AddPathToScore(ScoreElementType.FIRST_INVERSION, pos); break;
            case Inversion.第5音: AddPathToScore(ScoreElementType.SECOND_INVERSION, pos); break;
            case Inversion.第7音: AddPathToScore(ScoreElementType.THIRD_INVERSION, pos); break;
            default:
                System.Diagnostics.Debug.Assert(false);
                break;
            }

            switch (c.AddedTone) {
            case AddedToneType.None: break;
            case AddedToneType.Six:     AddPathToScore(ScoreElementType.CHORD_PLUS6, pos); break;
            case AddedToneType.SixFour: AddPathToScore(ScoreElementType.CHORD_PLUS46, pos); break;
            default: System.Diagnostics.Debug.Assert(false); break;
            }

            switch (c.Omission) {
            case Omission.None: break;
            case Omission.First: AddPathToScore(ScoreElementType.CHORD_STRIKE, pos); break;
            }

            if (c.Is準固有和音) {
                AddPathToScore(ScoreElementType.CHORD_CIRCLE, pos);
            }

            switch (c.AlterationType) {
            case AlterationType.None: break;
            case AlterationType.Raised: AddPathToScore(ScoreElementType.CHORD_RAISED, pos); break;
            case AlterationType.Lowered: AddPathToScore(ScoreElementType.CHORD_LOWERED, pos); break;
            case AlterationType.Dorian: AddPathToScore(ScoreElementType.CHORD_PLUS, pos); break;
            case AlterationType.Naples: AddPathToScore(ScoreElementType.CHORD_MINUS, pos); break;
            }
        }

        private void DrawVerdict(StaffStatus ss, Chord c) {
            var pos = new Vector(ss.Pos.X + Constants.CHORD_VERDICT_OFFSET_X, ss.Pos.Y + Constants.CHORD_VERDICT_OFFSET_Y);
            AddTextToScoreCenter(c.Verdict.ToString(), Constants.CHORD_VERDICT_FONT_SIZE, pos, 0.0);
        }

        private void DrawCadence(StaffStatus ss, Chord c) {
            if (c.TerminationType == TerminationType.None) {
                return;
            }

            var pos = new Vector(ss.Pos.X + Constants.CHORD_TERMINATION_X, ss.Pos.Y + Constants.CHORD_TERMINATION_Y);
            AddTextToScoreCenter(c.TerminationType.ToString(), Constants.CHORD_VERDICT_FONT_SIZE, pos, 1.0);
        }

        private void AddChordFocus(StaffStatus ss, Chord c) {
            var vs = new VirtualShape(VirtualShape.Type.Rectangle, new Rect(
                    ss.Pos.X - Constants.CHORD_WIDTH_HALF_X, ss.Pos.Y + Constants.CHORD_FOCUS_OFFSET_Y_TOP,
                    Constants.CHORD_FOCUS_WIDTH, Constants.CHORD_FOCUS_OFFSET_Y_BOTTOM - Constants.CHORD_FOCUS_OFFSET_Y_TOP));
            vs.ForegroundBrush = Brushes.Transparent;
            vs.StrokeDashArray = new DoubleCollection(){ 4.0, 2.0 };
            vs.StrokeThickness = 2.0;
            vs.Stroke = Brushes.Black;
            vs.Focusable = true;
            mScoreCanvas.AddVirtualChild(vs);
        }

        private static ScoreElementType[][] mInternalKeyPathArray = {
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_1 }, // I調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2 }, ///< II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_MINUS }, ///< -II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3 }, ///< III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_4 }, ///< IV調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_5 }, ///< V調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6 }, ///< VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7 }, ///< VII調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_UP }, ///< VII↑調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_1, ScoreElementType.INTERNAL_KEY_CIRCLE }, ///< ○I調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_CIRCLE_MINUS }, ///< ○-II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_CIRCLE }, ///< ○III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_4, ScoreElementType.INTERNAL_KEY_CIRCLE }, ///< ○IV調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_5, ScoreElementType.INTERNAL_KEY_CIRCLE }, ///< ○V調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_CIRCLE }, ///< ○VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_CIRCLE }, ///< ○VII調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_1, ScoreElementType.INTERNAL_KEY_PLUS }, ///< ＋I調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_PLUS }, ///< ＋II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_PLUS }, ///< ＋III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_4, ScoreElementType.INTERNAL_KEY_PLUS }, ///< ＋IV調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_5, ScoreElementType.INTERNAL_KEY_PLUS }, ///< ＋V調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_PLUS }, ///< ＋VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_PLUS, ScoreElementType.INTERNAL_KEY_UP }, ///< ＋VII↑調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_PLUS, ScoreElementType.INTERNAL_KEY_P }, ///< ＋IIIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_PLUS, ScoreElementType.INTERNAL_KEY_P }, ///< ＋VIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_PLUS, ScoreElementType.INTERNAL_KEY_UP_P }, ///< ＋VII↑p調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_TRIANGLE }, ///< △II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_TRIANGLE }, ///< △III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_TRIANGLE }, ///< △VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_TRIANGLE, ScoreElementType.INTERNAL_KEY_UP }, ///< △VII↑調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE }, ///< ＋△II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE }, ///< ＋△III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE }, ///< ＋△VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE, ScoreElementType.INTERNAL_KEY_UP }, ///< ＋△VII↑調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE, ScoreElementType.INTERNAL_KEY_P }, ///< ＋△IIIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE, ScoreElementType.INTERNAL_KEY_P }, ///< ＋△VIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_PLUS_TRIANGLE, ScoreElementType.INTERNAL_KEY_UP_P }, ///< ＋△VII↑p調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_MINUS_MINUS }, ///< －－II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_MINUS }, ///< －III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_MINUS }, ///< －VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_MINUS }, ///< －VII調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_MINUS_MINUS, ScoreElementType.INTERNAL_KEY_P }, ///< －－IIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_MINUS, ScoreElementType.INTERNAL_KEY_P }, ///< －IIIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_MINUS, ScoreElementType.INTERNAL_KEY_P }, ///< －VIp調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE_MINUS }, ///< －○－II調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE }, ///< －○III調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE }, ///< －○VI調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_7, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE }, ///< －○VII調

            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_2, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE_MINUS, ScoreElementType.INTERNAL_KEY_P }, ///< －○－IIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_3, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE, ScoreElementType.INTERNAL_KEY_P }, ///< －○IIIp調
            new ScoreElementType[] { ScoreElementType.INTERNAL_KEY_6, ScoreElementType.INTERNAL_KEY_MINUS_CIRCLE, ScoreElementType.INTERNAL_KEY_P }, ///< －○VIp調

        };
    }
}
